/***************************************************************************
 *   Copyright (C) 2010 by Stanislaw Jakiel,,,   *
 *   staszek@localhost   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
//NIE ODPOWIADAM ZA EWENTUALNE SZKODY DOKONANE PRZY UZYCIU MOJEGO PROGRAMU
// TYLKO DO CELOW EDUKACYJNYCH

// uruchomic jako admin ew sudo
// w przypadku gdy program otworzy nieprawidlowy interfejs sieciowy zamknac go wczesniej recznie
// opcja kompilatora -lpcap

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <pcap/pcap.h>
#include <string.h>
#include "sniffer.h"

char* inet_ntoa2(int ina) {
	static char buf[4*sizeof("123")]; //5*
	unsigned char* ucp = (unsigned char*)&ina;
	sprintf(buf,"%d.%d.%d.%d",ucp[0]&0xFF,ucp[1]&0xFF,ucp[2]&0xFF,ucp[3]&0xFF);
	return buf;
}

void dump(const unsigned char* data_buffer,const unsigned int length) { // zrzut pamieci
	unsigned char byte;
	unsigned int i,j;
	for(i=0;i<length;i++) {
		byte = data_buffer[i];
		printf("%02x ",data_buffer[i]);
		if((i%16 == 15)||(i == length -1)) {
			for(j=0;j<15-(i%16);j++)
				printf("    ");
			printf("| ");
			for(j=(i-(i%16));j<=i;j++) {
				byte = data_buffer[j];
				if((byte > 31) && (byte < 127))
					printf("%c",byte);
				else
					printf(".");
			}
			printf("\n");
		}
	}
}

void processPacket(u_char* arg, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
	// pkthdr zawiera podstawowe informacje o schwytanym pakiecie
	int pkt_data_len,i=0,type=0;
	int* counter = (int*) arg; // znacznik uzytkownika - licznik
	arphdr_t* arpheader = NULL;
	struct ether_header *eptr;  // naglowek ethernet
	struct ip_hdr* iph;  //naglowek IP
	struct tcp_hdr* tcph; //naglowek TCP
	u_char* ptr,*pkt_data; // pkt_data to poczatek danych

	eptr = (struct ether_header *) packet; // pierwsza idzie warstwa linku

	printf("========================================\n");
	printf("Received packet size: %d\n",pkthdr->len);
	printf("Packet count: %d\n",++(*counter));
	printf("LAYER 2 DECODE:\n");
	if (ntohs (eptr->ether_type) == ETHERTYPE_IP) {
		type =0;
        	printf("  Ethernet type hex: %x, dec: %d is an IP packet\n", ntohs(eptr->ether_type),ntohs(eptr->ether_type));
    	} else  if (ntohs (eptr->ether_type) == ETHERTYPE_ARP) {
		type =1;
        	printf("  Ethernet type hex:%x dec:%d is an ARP packet\n", ntohs(eptr->ether_type), ntohs(eptr->ether_type));
	} else if(ntohs (eptr->ether_type) == ETHERTYPE_REVARP) {
		type = 2;
		printf("  Ethernet type hex: %x, dec: %d is an IP packet\n", ntohs(eptr->ether_type),ntohs(eptr->ether_type));
	} else {
		type = 3;
        	printf("  Ethernet type %x not IP\n", ntohs(eptr->ether_type));
	}

	printf("  Layer 2 basic info:\n");
	ptr = eptr->ether_dhost;
    	i = ETHER_ADDR_LEN;
    	printf("   Destination Address:  ");
    	do{
        	printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
    	}while(--i>0);
    	printf("\n");

    	ptr = eptr->ether_shost;
    	i = ETHER_ADDR_LEN;
    	printf("   Source Address:  ");
    	do{
        	printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
    	}while(--i>0);
    	printf("\n");
//----------------------koniec warstwy linku----------------------------------------------------------
	printf("LAYER 3 DECODE:\n");
	switch(type) {
		case 0:
			iph = (struct ip_hdr*)(packet+14); // ip zaczyna sie po warstwie 2

			printf("  Sender IP: %s\n",inet_ntoa2(iph->ip_src_addr));
			printf("  Target IP: %s\n",inet_ntoa2(iph->ip_dest_addr));
			printf("  Type: %u\n",(u_int)iph->ip_type);
			printf("  ID: %hu\n  IP length: %hu\n",ntohs(iph->ip_id),ntohs(iph->ip_len));
			//---------------------------------------------koniec warstwy sieci
			tcph = (struct tcp_hdr*)(packet+14+sizeof(struct ip_hdr));
			printf("LAYER 4 DECODE\n");
			printf("  Source port: %hu\t",ntohs(tcph->tcp_src_port));
			printf("  Destination port: %hu\n",ntohs(tcph->tcp_dest_port));
			printf("  Seq #: %u\tAck #: %u\n",ntohl(tcph->tcp_seq),ntohl(tcph->tcp_ack));
			printf("  Type: ");
			if(tcph->tcp_flags & TCP_FIN)
			  printf("FIN\n");
			else if(tcph->tcp_flags & TCP_SYN)
			  printf("SYN\n");
			else if(tcph->tcp_flags & TCP_RST)
			  printf("RST\n");
			else if(tcph->tcp_flags & TCP_PUSH)
			  printf("PUSH\n");
			else if(tcph->tcp_flags & TCP_ACK)
			  printf("ACK\n");
			else if(tcph->tcp_flags & TCP_URG)
			  printf("URG\n");
			else
			  printf("N/A\n");
			//----------------------------------koniec warstwy transportowej
			//----------------------------------PAYLOAD
			pkt_data = (u_char*)packet+ETHER_HDR_LEN+sizeof(struct ip_hdr)+ ( 4 * tcph->tcp_offset);
			pkt_data_len = pkthdr->len - ETHER_HDR_LEN - sizeof(struct ip_hdr) - ( 4 * tcph->tcp_offset);
			printf("PAYLOAD\n");
			if(pkt_data_len > 0)
			  dump(pkt_data,pkt_data_len);
			else
			  printf("  NO DATA\n");
		break;
		case 1:
			arpheader = (struct arphdr*)(packet+14);

			printf("  --------------------\n");
			printf("  Hardware type: %s\n", ((ntohs(arpheader->htype) == 1) ? " Ethernet ":" Unknown "));
			printf("  Protocol type: %s\n", ( (ntohs(arpheader->ptype) == 0x0800) ? " IPv4 ":" Unknown " ));
			printf("  Operation: %s\n",( (ntohs(arpheader->oper)==ARP_REQUEST) ? " ARP Request ":" ARP Reply "));

			if((ntohs(arpheader->htype)== 1 && ntohs(arpheader->ptype) == 0x0800)) {
				printf("   Sender MAC: ");
				for(i = 0;i<6;i++) {
					printf("%02X:",arpheader->sha[i]);
				}
				printf("\n   Sender IP: ");
				for(i =0;i<4;i++) {
					printf("%d.",arpheader->spa[i]);
				}
				printf("\n   Target MAC: ");
				for(i =0;i<6;i++) {
					printf("%02X:",arpheader->tha[i]);
				}
				printf("\n   Target IP: ");
				for(i =0;i<4;i++) {
					printf("%d.",arpheader->tpa[i]);
				}
				printf("\n");
			}
		break;
		case 2:
			printf("  RARP Package\n");
		break;
		case 3:
			printf("  UNKNOWN PACKET\n");
		break;
		default:
			printf("CANNOT SEE ME\n");
			exit(1);

	}
	return;
}

int main(int argc, char *argv[])
{
	int count=0;
	bpf_u_int32 netaddr=0,mask=0;
	struct bpf_program filter;
	struct in_addr addr;

	char errbuf[PCAP_ERRBUF_SIZE], *device = NULL,*net_p=0,*mask_p=0;
	memset(errbuf,0,PCAP_ERRBUF_SIZE);

	net_p = (char*) calloc(12,sizeof(char));
	mask_p = (char*) calloc(12,sizeof(char));

	pcap_t* descr = NULL; //urzadzenie

	const unsigned char* packet = NULL;



//------------------------------
	printf("STARTUP\n");
	printf(" -GETTING DEVICE: ");
	device = pcap_lookupdev(errbuf);

	if( !device) {
		printf("ERROR\n");
		printf(errbuf);
		exit(1);
	} else {
		printf("SUCCESS: ");
		printf(device);
		printf("\n");
	}

	printf(" -LOOKUPNET: ");

	pcap_lookupnet(device,&netaddr,&mask,errbuf); //znajdz maske i adres dla device
	printf("SUCCESS: ");
	addr.s_addr = netaddr; //IP
	net_p = inet_ntoa(addr); //presentation
	if(net_p == NULL) {
		printf("INET_NTOA ERROR\n");
		exit(1);
	}
	printf("NET: %s, ",net_p);

	addr.s_addr = mask;
	mask_p = inet_ntoa(addr);
	if(mask_p == NULL) {
		printf("INET_NTOA ERROR\n");
		exit(1);
	}
	printf("MASK: %s\n",mask_p);


	printf(" =OPENING DEVICE: ");
	descr = pcap_open_live(device,MAXBYTES2CAPTURE,1,128,errbuf); // w tutorialu jest 0 ? chyba blad

	if(descr == NULL) {
		printf("ERROR\n");
		printf(errbuf);
		exit(1);
	} else {
		printf("SUCCESS\n");
	}

	printf(" -DATALINK: %d\n",pcap_datalink(descr));

	if(argc == 2) {
//	lapiemy wszystkie pakiety i je dekodujemy
	  printf(" -COMPILING REGEX: ");
/*	  pcap_compile(descr,&filter,"(dst host 192.168.1.13) or (src host 192.168.1.13)",1,mask);*/
  	  pcap_compile(descr,&filter,"arp",1,mask);
	  printf("SUCCESS\n");
	  printf(" -SETTING UP FILTER: ");
	  pcap_setfilter(descr,&filter);
	  printf("SUCCESS\n");
	}
	printf("STARTING\n");

	pcap_loop(descr,-1,processPacket,(u_char*)&count);

	return EXIT_SUCCESS;
}
